const axios = require('axios');
import { v4 as uuid } from 'uuid';
import { stringify as qs_stringify } from './querystring';

const REQUEST_ERROR = 1502;
const k = function(){};
const isFunction = (f)=>{ return ({}).toString.call(f) == "[object Function]";}
const isObject = (f)=>{ return ({}).toString.call(f) == "[object Object]";}


const BundleFramework = function(opts){ return this.init.apply(this, arguments) }

BundleFramework.prototype = {
	bundle: true,
	actions: {},
	baseURL: '',
	bundleURL: '',
	timeout: 5000,
	__bundle_tm:[],
	__bundle_queue: [],
	__requestHeaderMidware: null,
	__responseHeaderMidware: null,
	__errorMidware: null,

	init: function(opts){
		opts = Object.assign({
			bundle: true,
			actions: {},
			baseURL: "",
			timeout: 5000,
			bundleTimeout: 10000,
			useRequestHeaders: function(options){ return {}},
			useResponseHeaders: function(header, options){ },
			useError: function(errcb){ return errcb; },
		}, opts || {});
		this.timeout = opts.timeout;
		this.bundle = opts.bundle;
		this.bundleTimeout = opts.bundleTimeout;
		this.actions = opts.actions;
		this.baseURL = (opts.baseURL+"/").replace(/\/\/$/, "/");
		this.bundleURL = opts.bundleURL || (this.baseURL+"bundle");
		this.__requestHeaderMidware = opts.useRequestHeaders;
		this.__responseHeaderMidware = opts.useResponseHeaders;
		this.__errorMidware = opts.useError;
	},

	buildActions: function(){
		let that = this;
        let methods = {};
        let acts = this.actions;
        for(var act in acts){
            methods[act] = (function(act, args, params, options){
                args = [].slice.call(arguments);
                params = acts[act];
                options = params.slice(-1)[0];
                if(options.type=='json') options.method="POST";
                options.method = options.method || "GET";
                
                return function(args, has_options){
                    args = [].slice.call(arguments);
                    has_options = false;
                    for(var i=0,l=args.length; i<l; i++){
                        if(isObject(args[i])){
                            let x = i;
                            has_options = true;
                            if(isObject(args[i+1])){
                                x = i+1;
                            }
                            args[x] = Object.assign({}, options, args[x]);
                            break;
                        }
                    }
                    // 调用参数没有options, 插入options
                    if(!has_options){
                        has_options = l;
                        for(i=0; i<l; i++){
                            if(isFunction(args[i])){
                                has_options = i;
                                break;
                            }
                        }
                        if(has_options<0) has_options = 0;
                        args = args.slice(0, has_options).concat([options], args.slice(has_options));
                    }
                    return that.request.apply(that, [options.method, act].concat(args));
                }
            })(act);
        }
        return methods;
	},

	__camelCaseHeaders: function(headers){
		// 修复SDK BUG
		// 部分header 键值会转化为小写
		// 即无论大小写, 转化为驼峰拼写法
		for(var k in headers){
			var camelcase = k.replace("_", "-").split("-").map(function(s){
				s = s.toLowerCase()
				s = s[0].toUpperCase() + s.substr(1)
			}).join("-")
			headers[camelcase] = headers[k];
		}
		return headers;
	},

	__handleRequestHeaders: function(options){
		let ext = options.headers || {};
		let headers = Object.assign(this.__requestHeaderMidware() || {}, ext);
        return headers;
	},
	__handleResponseHeaders: function(header, options){
		return this.__responseHeaderMidware.call(this, header, options);
	},

	get: function(){ return this.request.apply(this, ['GET'].concat(arguments)); },

	post: function(){ return this.request.apply(this, ['POST'].concat(arguments)); },

	request: function(method, action, args, options, success, error, complete){
	    var that = this;
	    args = [].slice.call(arguments);
	    method = args.shift();
	    action = args.shift();
	    options = {};
	    success = error = complete = undefined;
	    var params = [];
	    args.forEach(function(v){
	        if(isFunction(v) || success){
	            if(!success) return (success = v);
	            if(error === undefined) return (error = v);
	            if(complete === undefined)  return (complete = v);
	            return false;
	        }
	        if(success || error || complete) return false;
	        params.push(v);
	    });

	    // 默认错误显示
	    error = that.__errorMidware.call(that, error || k, arguments);

	    args = params;
	    params = [];
	    // 提取params, options
	    args.forEach(function(v, i){
	        if(isObject(v)){
	            if(i==0 && args[i+1] && isObject(args[i+1])){
	                params = v;
	                options = args[i+1];
	                return false;
	            }
	            options = v;
	            return false;
	        }
	        params.push(v);
	    });

	    // 如果params不是字典
	    if(!isObject(params)){
	        // 未定义参数映射
	        if(!that.actions[action]) {
	            throw "Undefined action \""+action+"\" in ActionParams.";
	        }
	        let aparams = that.actions[action].slice(0);
	        aparams.pop();
	        // 参数溢出
	        if(params.length > aparams.length) {
	            throw "Too many params for action \""+action+"\".";
	        }
	        args = params;
	        params = {};
	        aparams.forEach((p,i, k, def)=>{
	            p = p.split("=");
	            k = p[0];
	            def = null;
	            if(p[1]){
	                try{
	                    def = JSON.parse(p[1]);
	                }catch(e){}
	            }
	            if(args[i]===null) return;
	            if(args[i]===undefined) {
	                params[k] = def;
	                return;
	            }
	            params[k] = args[i];
	        });
	    }

	    // 设置header时, 不使用bundle
	    if(options.headers){ options.bundle = false; }
	    // 默认options
	    options = Object.assign({
	    	baseURL: that.baseURL,
	        method: method,
	        bundle: true,
	        bundleTimeout: that.bundleTimeout,
	        timeout: that.timeout,
	        purely: false,
	    }, options);
	    if(that.bundle && options.bundle){
	        that.bundleRequest(uuid(), method, action, params, success, error, complete, options);
	        that.__bundle_tm.push(setTimeout(()=>{
	        	that.sendBundle();
	        }, options.bundle_timeout));
	    }else{
	        that.singleRequest(method, action, params, success, error, complete, options);
	    }
	},

	/**
	 * 发送单个请求
	 */
	singleRequest: function(method, action, params, success, error, complete, options){
		let that = this;
		axios({
		  method,
		  baseURL: options.baseURL,
		  url: options.url || action,
		  headers: that.__handleRequestHeaders(options),
		  data: method.toLowerCase() == "get" ? {}: params,
		  params: method.toLowerCase() == "get" ? params: {},
		  paramsSerializer: function (params) {
		    return qs_stringify(params)
		  },
		  timeout: options.timeout || 5000
		}).then(function (res, data, headers) {
			headers = that.__camelCaseHeaders(res.headers);
			that.__handleResponseHeaders(headers, options);
			if(options.purely){
				return (success || k)(res.data, res, headers);
			}
			if(res.data.error){
				if(isFunction(error)){
					error(res.data.error);
				}else{
					throw res.data.error;
				}
				return;
			}
			(success || k)(res.data.data, res, headers);

		}).catch(function (err) {
			if(isFunction){
				if (err.response) {
			      	return error({code:err.response.status, message:"HTTPError: "+err.response.status});
			    } else if (err.request) {
			    	return error({code: REQUEST_ERROR, message:"XmlRequestInstanceError"});
			    } else {
			    	throw err;
			    }
			}else{
				throw err;
			}
		    
	  	}).finally(complete || k);
	},

	/**
	 * 打包追加请求
	 */
	bundleRequest: function(queue, method, action, params, success, error, complete, options){
	    // slug 防止多次并发同样请求
	    var slug = [method,action, JSON.stringify(params)].join(":")
	    ,   duplicated = null;
	    // 重复检测
	    this.__bundle_queue.forEach(function(q){
	        if(q.slug == slug){ duplicated=q; return true; }
	    });
	    if(duplicated){
	    	console.log("Duplicated", action)
	        let dsuccess = duplicated.success
	        ,   derror = duplicated.error
	        ,   dcomplete = duplicated.complete;
	        duplicated.success = function(){
	            (dsuccess||k).apply(this, arguments);
	            (success||k).apply(this, arguments);
	        };
	        duplicated.error = function(){
	            (derror||k).apply(this, arguments);
	            (error||k).apply(this, arguments);
	        };
	        duplicated.complete = function(){
	            (dcomplete||k).apply(this, arguments);
	            (complete||k).apply(this, arguments);
	        };
	    }else{
	        this.__bundle_queue.push({ queue, slug, method, action, params, success, error, complete , options });
	    }
	},
	
	/**
	 * 发送捆绑包数据
	 */
	sendBundle: function(){
		let that = this;
	    while(this.__bundle_tm.length){
	        clearTimeout(this.__bundle_tm.pop())
	    }
	    var send_queue = this.__bundle_queue.splice(0);
	    var options = send_queue[0].options;
	    options.timeout = that.bundleTimeout;

	    // 捆绑请求异常, 发起独立请求
	    const bundle_failed = function(){
	        send_queue.forEach(function(b){
	            that.singleRequest(b.method, b.action, b.params, b.success, b.error, function(){
	                (b.complete || k).apply(null, [].slice.call(arguments,0));
	            }, b.options);
	        })
	    }

	    const pop_send_queue = (qid)=>{
	        var queue = null;
	        send_queue.forEach((q, i) => {
	            if(q.queue == qid){
	                queue = send_queue.splice(i, 1)[0];
	                return false;
	            }
	        });
	        return queue;
	    }

	    // 如果队列只一个请求, 则直接请求
	    if(send_queue.length<=1) {
	        return bundle_failed();
	    }

	    var bundle_data = send_queue.map(function(b){
	        return { queue:b.queue, action:b.action, params:b.params };
	    });


	    axios({
			method: 'POST',
			url: that.bundleURL,
			headers: that.__handleRequestHeaders(Object.assign({}, options, {"Content-Type":"application/json"})),
			data: {bundle: bundle_data},
			timeout: that.bundleTimeout
			// paramsSerializer: function (params) {
			//   return JSON.stringify(params);
			// },
		}).then(function (res, data, headers) {
			headers = that.__camelCaseHeaders(res.headers);
			that.__handleResponseHeaders(headers, options);
	
			if(res.data.error || !res.data.data.length){
				return bundle_failed();
			}
			// 遍历处理返回的队列数据
			res.data.data.forEach(function(r){
				if(!r.queue) return;
				// 提取queue唯一对应的请求时绑定的各个回调函数
				var q = pop_send_queue(r.queue);
				if(!q) return;
				if(r.error){ // 独立action错误
					(q.error||k)(r.error);
					(q.complete||k)();
					return;
				}
				// success 回调
				(q.success||k)(r.data, r, headers);
				(q.complete||k)();
			});
			// 单独跑未成功匹配的队列
			bundle_failed();
		}).catch(function (err) {
			bundle_failed();
	  	});
	}
}
export default function(opts){ return new BundleFramework(opts)};
